using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL.Fabric
{
    public class Input
    {
        protected NodeVector nvNodeVector;
        protected string sName;
        protected int iWidth;

        protected Block bkParentBlock;

        protected Dictionary<string, Output> dicConnections = new Dictionary<string,Output>();
        protected Dictionary<string, ControlInput> dicConnectionSelect = new Dictionary<string,ControlInput>();
        protected Dictionary<string, Output> dicORConnections = new Dictionary<string, Output>();

        internal bool IgnoreConnectionError=false;

        public Input(Block pbkParentBlock,string psName, int piWidth)
        {
            iWidth = piWidth;
            bkParentBlock = pbkParentBlock;
            sName = psName;
        }

        internal void LinkNodeVector(NodeVector pnvNodeVector)
        {
            nvNodeVector = pnvNodeVector;
        }

        public int Width
        {
            get { return iWidth; }
            set { iWidth = value; }
        }

        public NodeVector NodeVector
        {
            get { return nvNodeVector; }
        }

        public string Name
        {
            get { return sName; }
        }

        public override string ToString()
        {
            return bkParentBlock.ToString() + "." + sName;
        }

        internal string ValidateStructure()
        {
            string sError = "";
            if (nvNodeVector == null)
                return SharedUtility.BuildError(this.ToString(),"Node Vector Is Null");
            if (iWidth != nvNodeVector.Width)
                sError = SharedUtility.BuildError(this.ToString(), "Node Vector Width is " + nvNodeVector.Width.ToString() + ", Expected " + iWidth.ToString());

            if (dicConnections.Count == 0 && dicORConnections.Count == 0 && !IgnoreConnectionError)
                sError += SharedUtility.BuildError(this.ToString(), "Node Vector is not connected.");

            foreach (Output oOutput in dicConnections.Values)
            {
                if (oOutput.Width != iWidth)
                    sError += SharedUtility.BuildWarning(this.ToString(), "Connection {" + oOutput.ToString() + "} is width " + oOutput.Width.ToString() + ", Expected " + iWidth.ToString());
            }
            return sError;
        }

        public string FullName
        {
            get { return this.ToString().Replace('.', '_'); }
        }

        internal virtual void JoinOR(Output poOutput)
        {
            if (dicConnections.ContainsKey("unique"))
                bkParentBlock.ReportError("Input {" + ToString() + "} is not unique because of output {" + poOutput.ToString() + "}");

            dicORConnections.Add(poOutput.Name, poOutput);
        }

        internal virtual void JoinUnique(Output poOutput)
        {
            if (dicConnections.Count > 0)
                bkParentBlock.ReportError("Input {" + ToString() + "} is not unique when trying to add output {" + poOutput.ToString() + "}");
            if (dicORConnections.Count > 0)
                bkParentBlock.ReportError("Input {" + ToString() + "} is not unique when trying to add output {" + poOutput.ToString() + "}");

            dicConnections.Add("unique", poOutput);
        }

        internal virtual void JoinDefault(Output poOutput)
        {
            if (dicConnections.ContainsKey("unique"))
                bkParentBlock.ReportError("Input {" + ToString() + "} is not unique because of output {" + poOutput.ToString() + "}");
            dicConnections.Add("default", poOutput);
        }

        internal virtual ControlInput JoinWithSelect(Output poOutput)
        {
            if (dicConnections.ContainsKey("unique"))
                bkParentBlock.ReportError("Input {" + ToString() + "} is not unique because of output {" + poOutput.ToString() + "}");

            string sControlName = Name + "_SELINPUT_" + poOutput.FullName;
            ControlInput oInput;
            if (bkParentBlock.ControlInputs.ContainsKey(sControlName))
            {
                oInput = bkParentBlock.ControlInputs[sControlName];
            }
            else
            {
                oInput = bkParentBlock.CreateControlInput(sControlName);
            }
            if (!dicConnections.ContainsKey(oInput.Name))
                dicConnections.Add(oInput.Name, poOutput);
            if (!dicConnectionSelect.ContainsKey(oInput.Name))
                dicConnectionSelect.Add(oInput.Name, oInput);
            return oInput;
        }

        internal virtual void ExpandJoin(Component poParentComponent)
        {
            Output oDefaultOutput = null;
            NodeVector nvOut;

            if (dicConnections.Count == 0 && dicORConnections.Count == 0)
            {
                if (!IgnoreConnectionError)
                    bkParentBlock.ReportWarning("Input {" + ToString() + "} is floating, with no connections.");
                return;
            }

            if (dicConnections.Count > 0)
            {
                foreach (Output oOutput in dicConnections.Values)
                {
                    oDefaultOutput = oOutput;
                    break;
                }
                if (dicConnections.ContainsKey("default"))
                {
                    oDefaultOutput = dicConnections["default"];
                }

                if (dicConnections.Count == 1)
                {
                    nvOut = oDefaultOutput.NodeVector;
                }
                else
                {
                    AssignmentMux oMux = new AssignmentMux(poParentComponent, FullName + "_mux", iWidth, oDefaultOutput.NodeVector);
                    foreach (string sConnection in dicConnections.Keys)
                    {
                        if (sConnection != oDefaultOutput.Name && sConnection != "default")
                        {
                            ControlInput oSelect = dicConnectionSelect[sConnection];
                            Output oOutput = dicConnections[sConnection];
                            oMux.AddAssignment(oSelect.NodeVector, oOutput.NodeVector);
                        }
                    }
                    nvOut = oMux.OutputNodes;
                }
            }
            else
            {
                nvOut = null;
            }
            if (dicORConnections.Count == 0)
            {
                nvNodeVector.Join(nvOut);
            }
            else
            {
                if (nvOut == null)
                {
                    if (dicORConnections.Count == 1)
                    {
                        foreach (Output oOutput in dicORConnections.Values)
                        {
                            nvNodeVector.Join(oOutput.NodeVector);
                        }
                    }
                    else
                    {
                        OrGate oORGate = new OrGate(poParentComponent, FullName + "_OR", dicORConnections.Count);
                        nvNodeVector.Join(oORGate.OutputNodes);
                        int iIdx = 0;
                        foreach (Output oOutput in dicORConnections.Values)
                        {
                            oORGate.InputNodes[iIdx, iIdx].Join(oOutput.NodeVector);
                            iIdx++;
                        }
                    }
                }
                else
                {
                    OrGate oORGate = new OrGate(poParentComponent, FullName + "_OR", dicORConnections.Count + 1);
                    nvNodeVector.Join(oORGate.OutputNodes);
                    int iIdx = 0;
                    foreach (Output oOutput in dicORConnections.Values)
                    {
                        oORGate.InputNodes[iIdx, iIdx].Join(oOutput.NodeVector);
                        iIdx++;
                    }
                    oORGate.InputNodes[iIdx, iIdx].Join(nvOut);
                }
            }
        
        }
    }
}
